package cc.rengu.igas.channel.wechat.core.service.base;

import cc.rengu.igas.channel.wechat.common.constant.*;
import cc.rengu.igas.channel.wechat.common.enums.RespCodeEnum;
import cc.rengu.igas.channel.wechat.core.model.ChnlBizResponse;
import cc.rengu.igas.channel.wechat.facade.base.BaseRequest;
import cc.rengu.jradp.schedule.Schedule;
import cc.rengu.oltp.service.base.RadpService;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.*;
import cc.rengu.oltp.service.common.dao.impl.*;
import cc.rengu.oltp.service.common.entity.*;
import cc.rengu.oltp.service.common.enums.ChannelEnum;
import cc.rengu.oltp.service.common.enums.OltpRpcdEnum;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.realize.SysParamService;
import cc.rengu.oltp.service.realize.impl.SysParamServiceImpl;
import cc.rengu.oltp.utility.util.ConvertUtil;
import cc.rengu.oltp.utility.util.RedisUtil;
import cc.rengu.oltp.utility.util.XmlTreeUtil;
import cc.rengu.utility.communication.HttpMethod;
import com.alibaba.fastjson.JSON;

import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.*;

/**
 * 消息推送业务处理基类
 */
public abstract class MsgPushService<T> extends RadpService {
    @Override
    protected int bizInit(String s) {
        return 0;
    }

    @Override
    protected int bizProcess(String s, int i) {
        try {
            /* Step1：通道(交易)权限检查 */
            rglog.info("---------------> 通道(交易)权限检查 <---------------");
            channelPermissionCheck();
            /* Step2：业务处理前实现接口 */
            rglog.info("---------------> 业务处理前处理 <---------------");
            T request = beforeBizProcess();
            rglog.error("请求对象request:<{}>", JSON.toJSONString(request));
            /* Step3：业务逻辑处理 */
            rglog.info("---------------> 业务逻辑处理 <---------------");
            ChnlBizResponse response = callBizService(request);
            /* Step4：业务处理后实现接口 */
            rglog.info("---------------> 业务处理后处理 <---------------");
            afterBizProcess(response);
        } catch (Exception e) {
            XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
            String respCode;
            String respDesc;
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            e.printStackTrace(new PrintWriter(byteArrayOutputStream, true));
            rglog.error("业务异常信息:<{}>", byteArrayOutputStream.toString());
            if (e instanceof BizException) {
                respCode = ((BizException) e).getCode();
                respDesc = e.getMessage();
                rglog.error("业务处理失败，业务返回码:<{}>，业务返回信息:<{}>", respCode, respDesc);
            } else {
                rglog.error("系统处理失败，返回信息:<{}>", e);
                respCode = OltpRpcdEnum.OLTP_PROCESS_ERROR.getRespCode();
                respDesc = OltpRpcdEnum.OLTP_PROCESS_ERROR.getRespDesc();
            }
            try {
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.RESP_CODE, respCode);
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.RESP_DESC, respDesc);
            } catch (Exception ex) {
                ex.printStackTrace(new PrintWriter(byteArrayOutputStream, true));
                rglog.error("设置节点值异常信息:<{}>", byteArrayOutputStream.toString());
                return -1;
            }

        }
        return 0;
    }

    @Override
    protected int bizTerminate(String s) {
        return 0;
    }

    /**
     * 业务调用前处理
     *
     * @return 业务处理请求结构
     */
    protected abstract T beforeBizProcess() throws Exception;

    /**
     * 业务处理服务调用
     *
     * @param request 业务处理信息
     * @return 业务处理应答结果
     */
    protected abstract ChnlBizResponse callBizService(T request) throws Exception;

    /**
     * 业务调用后处理
     *
     * @param response 业务响应结果
     */
    protected abstract void afterBizProcess(ChnlBizResponse response) throws Exception;

    /**
     * 通道、交易权限权限
     *
     * @throws Exception 业务处理异常
     */
    private void channelPermissionCheck() throws Exception {
        BaseRequest baseRequest = new BaseRequest();
        ConvertUtil.convertOutput(baseRequest);
        rglog.error("请求报文头:<{}>", JSON.toJSONString(baseRequest));
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String instId = baseRequest.getHeader().getInstId();
        String txnNum = baseRequest.getHeader().getTxnNum();
        if (!xmlTreeUtil.isExist(TreeNodeConstant.INST_ID)) {
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.INST_ID, instId);
        }
        String channelId = ChannelEnum.WXZF.getChannelType();
        /* 通道状态检查 */
        String dstChannelKey = instId + channelId;
        DstChannelInfo dstChannelInfo = JSON.parseObject(RedisUtil.hashGet(AppParamConstant.CHANNEL_ISS_INFO_CACHE, dstChannelKey), DstChannelInfo.class);
        if (null == dstChannelInfo) {
            DstChannelInfoMapper dstChannelInfoMapper = new DstChannelInfoMapperImpl();
            dstChannelInfo = dstChannelInfoMapper.selectDstChannelInfoByPrimaryKey(instId, channelId);
        }
        if (null == dstChannelInfo || !dstChannelInfo.getDstChannelStatus().equalsIgnoreCase(AppParamConstant.YES)) {
            rglog.error("支付渠道<{}-{}>未配置或已停用！", channelId, (null == dstChannelInfo) ? "" : dstChannelInfo.getDstChannelName());
            throw new BizException(OltpRpcdEnum.CHANNEL_DISABLED);
        }
        xmlTreeUtil.setXmlTreeObjectValue(WechatTreeNodeConstant.DST_CHANNEL_INFO, dstChannelInfo);
        /*获取通道参数配置*/
        DstChannelCfgMapper dstChannelCfgMapper = new DstChannelCfgMapperImpl();
        DstChannelCfg dstChannelCfg = dstChannelCfgMapper.selectDstChannelCfgByPrimaryKey(instId, channelId, "BSPS");
        if (dstChannelCfg == null) {
            throw new BizException(OltpRpcdEnum.CHANNEL_DISABLED);
        } else {
            xmlTreeUtil.setXmlTreeObjectValue(WechatTreeNodeConstant.DST_CHANNEL_CFG, dstChannelCfg);
        }
        if (instId.isEmpty() || txnNum.isEmpty() || channelId.isEmpty()) {
            rglog.error("instId:<{}> or txnNum:<{}> or channelId:<{}> is null.", instId, txnNum, channelId);
            throw new BizException(OltpRpcdEnum.PARAM_IS_EMPTY_OR_NULL);
        }
        String dstChannelAuthKey = instId.trim() + channelId.trim() + txnNum.trim();
        DstTxnAuthCfg dstTxnAuthCfg = JSON.parseObject(RedisUtil.hashGet(AppParamConstant.CHANNEL_TXN_CACHE, dstChannelAuthKey), DstTxnAuthCfg.class);
        if (null == dstTxnAuthCfg) {
            DstTxnAuthCfgMapper dstTxnAuthCfgMapper = new DstTxnAuthCfgMapperImpl();
            dstTxnAuthCfg = dstTxnAuthCfgMapper.selectDstTxnAuthCfgByPrimaryKey(instId, channelId, txnNum);
        }
        if (null == dstTxnAuthCfg || !dstTxnAuthCfg.getSupportFlag().equalsIgnoreCase(AppParamConstant.YES)) {
            rglog.error("支付通道<{}-{}>不支持此交易，通道交易码:<{}>", dstChannelInfo.getDstChannelId(), dstChannelInfo.getDstChannelName(), txnNum);
            throw new BizException(OltpRpcdEnum.TRANS_NOT_SUPPORT);
        }
        if (dstTxnAuthCfg.getMsgRegisterFlag() != null && !dstTxnAuthCfg.getMsgRegisterFlag().isEmpty()) {
            xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WECHAT_MSG_REG_FLAG, dstTxnAuthCfg.getMsgRegisterFlag());
        }
    }

    /**
     * 清除微信推送错误信息,获取新Token,并清除成功信息
     *
     * @param dstChannelCfg 支付通道信息,可不传,保留入参主要为了保证无法重写来覆盖业务服务类中的方法
     * @return 新Token
     * @throws Exception 异常信息
     */
    protected String getAccessToken(DstChannelCfg dstChannelCfg) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        if (null == dstChannelCfg) {
            dstChannelCfg = (DstChannelCfg) xmlTreeUtil.getXmlTreeObjectValue(WechatTreeNodeConstant.DST_CHANNEL_CFG, DstChannelCfg.class);
            if (null == dstChannelCfg) {
                rglog.debug("未配置支付通道信息.");
                throw new BizException(RespCodeEnum.CHANNEL_CONFIG_NOT_EXIST.getRespCode(), RespCodeEnum.CHANNEL_CONFIG_NOT_EXIST.getRespDesc());
            }
        }
        /*清除调用失败时的错误信息*/
        xmlTreeUtil.deleteXmlTreeNode(WechatTreeNodeConstant.WX_ERR_CODE);
        xmlTreeUtil.deleteXmlTreeNode(WechatTreeNodeConstant.WX_ERR_MSG);
        String secret = null;
        /* 根据通道安全计划获取安全计划 */
        SecPlanInfoMapper secPlanInfoMapper = new SecPlanInfoMapperImpl();
        List<SecPlanInfo> secPlanInfoList = secPlanInfoMapper.selectSecPlanInfoBySecPlanId(dstChannelCfg.getInstId(), dstChannelCfg.getSecPlanId());
        if (null != secPlanInfoList && !secPlanInfoList.isEmpty()) {
            Optional<SecPlanInfo> secPlanInfoOpt = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(WechatParamConstant.SECRET_KEY_TYPE)).findFirst();
            if (secPlanInfoOpt.isPresent()) {
                KeyInfoMapper keyInfoMapper = new KeyInfoMapperImpl();
                KeyInfo keyInfo = keyInfoMapper.selectKeyInfoByPrimaryKey(secPlanInfoOpt.get().getInstId() + secPlanInfoOpt.get().getSecPlanId() + secPlanInfoOpt.get().getSecPlanType());
                if (keyInfo != null) {
                    secret = keyInfo.getKeyValue();
                }
            }
        }
        if (secret == null) {
            rglog.error("获取服务商secret失败");
            throw new BizException(RespCodeEnum.GET_THIRD_MCHNT_KEY_ERROR.getRespCode(), RespCodeEnum.GET_THIRD_MCHNT_KEY_ERROR.getRespDesc());
        }
        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TXN_NUM, WechatTxnNumConstant.getaccesstoken);
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("appid", dstChannelCfg.getSubAppId());
        paramMap.put("secret", secret);
        paramMap.put("grant_type", WechatParamConstant.DEFAULT_WECHAT_TOKEN_GRANT_TYPE);
        SysParamService sysParamService = new SysParamServiceImpl();
        SysParam sysParam = sysParamService.getSysParamInfo(dstChannelCfg.getInstId(), WechatConfigConstant.WECHAT_CONF_NODE, WechatConfigConstant.WECHAT_ACCESS_TOKEN_URL);
        if (sysParam == null) {
            throw new BizException(RespCodeEnum.CONFIG_PARAM_NOT_EXIST.getRespCode(), RespCodeEnum.CONFIG_PARAM_NOT_EXIST.getRespDesc());
        }
        String requestUrl = generateHttpUrl(sysParam.getParamValue(), paramMap);
        xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.DEFINE_HTTP_URL_NODE, requestUrl);
        Schedule schedule = new Schedule();
        int iReturnCode = schedule.callService(WechatServiceConstant.WECHAT_PUBLIC_CLIENT_SERVICE, HttpMethod.GET);
        if (0 != iReturnCode) {
            rglog.error("调用微信公众号服务失败，iReturnCode:<{}>", iReturnCode);
            throw new BizException(RespCodeEnum.CALL_BIZ_SERVICE_ERROR.getRespCode(), RespCodeEnum.CALL_BIZ_SERVICE_ERROR.getRespDesc());
        }
        /* 判断调用微信是否成功 */
        String errcode = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_ERR_CODE);
        String errmsg = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_ERR_MSG);
        if (null != errcode) {
            rglog.error("调用微信公众号服务失败,errcode:<{}>,errmsg:<{}>", errcode, errmsg);
            throw new BizException(errcode, errmsg);
        }
        /*清除调用成功信息,供后续调用使用*/
        xmlTreeUtil.deleteXmlTreeNode(WechatTreeNodeConstant.WX_ERR_CODE);
        xmlTreeUtil.deleteXmlTreeNode(WechatTreeNodeConstant.WX_ERR_MSG);
        String accessToken = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.ACCESS_TOKEN);
        /*加载到redis缓存*/
        RedisUtil.onceSet(WechatParamConstant.ACCESS_TOKEN_CACHE_NAME + dstChannelCfg.getSubAppId(), accessToken, WechatParamConstant.ACCESS_TOKEN_EXPIRY_TIME);
        return accessToken;
    }

    /**
     * 根据配置拼装URL地址
     *
     * @param transUrl    URL地址
     * @param urlParamMap URL参数集合
     * @return URL地址
     */
    private String generateHttpUrl(String transUrl, Map<String, String> urlParamMap) {
        StringBuilder requestUrlParam = new StringBuilder();
        for (String key : urlParamMap.keySet()) {
            requestUrlParam.append(key).append("=").append(urlParamMap.get(key)).append("&");
        }
        return transUrl + requestUrlParam.substring(0, requestUrlParam.length() - 1);
    }
}
